package com.ljx.proxyMode.dynamicProxy;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 *
 * JDK动态代理
 *
 * 定义：
 * 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时，访问对象不适合或者不能直接引用目标对象，代理对象作为访问对象和目标对象之间的中介
 *
 * 1. 在测试类中通过代理对象调用sell()方法
 * 2. 根据多态的特性，执行的是代理类（$Proxy0）中的sell()方法
 * 3. 代理类（$Proxy0）中的sell()方法中又调用了InvocationHandler接口的子实现类对象的invoke方法
 * 4. invoke方法通过反射执行了真实对象所属类(TrainStation)中的sell()方法
 *
 * jdk代理和CGLIB代理
 * 使用CGLib实现动态代理，CGLib底层采用ASM字节码生成框架，使用字节码技术生成代理类，在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是，CGLib不能对声明为final的类或者方法进行代理，
 * 因为CGLib原理是动态生成被代理类的子类。在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后，在调用次数较少的情况下，JDK代理效率高于CGLib代理效率，
 * 只有当进行大量调用的时候，JDK1.6和JDK1.7比CGLib代理效率低一点，但是到JDK1.8的时候，JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理，如果没有接口使用CGLIB代理。
 * 动态代理和静态代理
 * 动态代理与静态代理相比较，最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理（InvocationHandler.invoke）。
 * 这样，在接口方法数量比较多的时候，我们可以进行灵活处理，而不需要像静态代理那样每一个方法进行中转。
 * 如果接口增加一个方法，静态代理模式除了所有实现类需要实现这个方法外，所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题
 *
 * 优点：
 * - 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用；
 * - 代理对象可以扩展目标对象的功能；
 * - 代理模式能将客户端与目标对象分离，在一定程度上降低了系统的耦合度；
 *
 * 缺点：
 * * 增加了系统的复杂度；
 *
 * 使用场景：
 *
 * 远程（Remote）代理
 * 本地服务通过网络请求远程服务。为了实现本地到远程的通信，我们需要实现网络通信，处理其中可能的异常。为良好的代码设计和可维护性，我们将网络通信部分隐藏起来，只暴露给本地服务一个接口，通过该接口即可访问远程服务提供的功能，而不必过多关心通信部分的细节。
 * 防火墙（Firewall）代理
 * 当你将浏览器配置成使用代理功能时，防火墙就将你的浏览器的请求转给互联网；当互联网返回响应时，代理服务器再把它转给你的浏览器。
 * 保护（Protect or Access）代理
 * 控制对一个对象的访问，如果需要，可以给不同的用户提供不同级别的使用权限。
 *
 *
 * @作者 LJX
 * @日期 2022/12/22 16:43
 */
public class JDKDynamicProxyTest {

    public static void main(String[] args) {
        //客户买票
        //调用代理工厂生成代理对象
        SellTickets proxyObject = ProxyFactory.getProxyObject();
        proxyObject.sell();
    }

}

/**
 * 卖票接口规范（抽象主题类）
 */
interface SellTickets{

    void sell();
}

/**
 * 火车站（真实主题类）
 */
class TrainStation implements SellTickets{

    @Override
    public void sell() {
        System.out.println("火车站卖票");
    }
}

/**
 * 代理工厂
 */
class ProxyFactory {

    private static TrainStation ts = new TrainStation();

    public static SellTickets getProxyObject(){
        return (SellTickets) Proxy.newProxyInstance(TrainStation.class.getClassLoader(), TrainStation.class.getInterfaces(),
                //实现InvocationHandler 匿名内部类
                (Object proxy, Method method, Object[] args) -> {
                    //代理方法增强
                    System.out.println("代理点收取额外费用（JDK）");
                    //执行目标对象方法
                    method.invoke(ts, args);
                    return null;
                }
        );
    }

}
